//===-- SymbolVendor.mm -----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/Symbol/SymbolVendor.h"

// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/Section.h"
#include "lldb/Core/Stream.h"
#include "lldb/Symbol/CompileUnit.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolFile.h"

using namespace lldb;
using namespace lldb_private;


//----------------------------------------------------------------------
// FindPlugin
//
// Platforms can register a callback to use when creating symbol
// vendors to allow for complex debug information file setups, and to
// also allow for finding separate debug information files.
//----------------------------------------------------------------------
SymbolVendor*
SymbolVendor::FindPlugin (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm)
{
    std::unique_ptr<SymbolVendor> instance_ap;
    SymbolVendorCreateInstance create_callback;

    for (size_t idx = 0; (create_callback = PluginManager::GetSymbolVendorCreateCallbackAtIndex(idx)) != nullptr; ++idx)
    {
        instance_ap.reset(create_callback(module_sp, feedback_strm));

        if (instance_ap.get())
        {
            return instance_ap.release();
        }
    }
    // The default implementation just tries to create debug information using the
    // file representation for the module.
    instance_ap.reset(new SymbolVendor(module_sp));
    if (instance_ap.get())
    {
        ObjectFile *objfile = module_sp->GetObjectFile();
        if (objfile)
            instance_ap->AddSymbolFileRepresentation(objfile->shared_from_this());
    }
    return instance_ap.release();
}

//----------------------------------------------------------------------
// SymbolVendor constructor
//----------------------------------------------------------------------
SymbolVendor::SymbolVendor(const lldb::ModuleSP &module_sp) :
    ModuleChild (module_sp),
    m_type_list(),
    m_compile_units(),
    m_sym_file_ap()
{
}

//----------------------------------------------------------------------
// Destructor
//----------------------------------------------------------------------
SymbolVendor::~SymbolVendor()
{
}

//----------------------------------------------------------------------
// Add a representation given an object file.
//----------------------------------------------------------------------
void
SymbolVendor::AddSymbolFileRepresentation(const ObjectFileSP &objfile_sp)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (objfile_sp)
        {
            m_objfile_sp = objfile_sp;
            m_sym_file_ap.reset(SymbolFile::FindPlugin(objfile_sp.get()));
        }
    }
}

bool
SymbolVendor::SetCompileUnitAtIndex (size_t idx, const CompUnitSP &cu_sp)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        const size_t num_compile_units = GetNumCompileUnits();
        if (idx < num_compile_units)
        {
            // Fire off an assertion if this compile unit already exists for now.
            // The partial parsing should take care of only setting the compile
            // unit once, so if this assertion fails, we need to make sure that
            // we don't have a race condition, or have a second parse of the same
            // compile unit.
            assert(m_compile_units[idx].get() == nullptr);
            m_compile_units[idx] = cu_sp;
            return true;
        }
        else
        {
            // This should NOT happen, and if it does, we want to crash and know
            // about it
            assert (idx < num_compile_units);
        }
    }
    return false;
}

size_t
SymbolVendor::GetNumCompileUnits()
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_compile_units.empty())
        {
            if (m_sym_file_ap.get())
            {
                // Resize our array of compile unit shared pointers -- which will
                // each remain NULL until someone asks for the actual compile unit
                // information. When this happens, the symbol file will be asked
                // to parse this compile unit information.
                m_compile_units.resize(m_sym_file_ap->GetNumCompileUnits());
            }
        }
    }
    return m_compile_units.size();
}

lldb::LanguageType
SymbolVendor::ParseCompileUnitLanguage (const SymbolContext& sc)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ParseCompileUnitLanguage(sc);
    }
    return eLanguageTypeUnknown;
}


size_t
SymbolVendor::ParseCompileUnitFunctions (const SymbolContext &sc)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ParseCompileUnitFunctions(sc);
    }
    return 0;
}

bool
SymbolVendor::ParseCompileUnitLineTable (const SymbolContext &sc)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ParseCompileUnitLineTable(sc);
    }
    return false;
}

bool
SymbolVendor::ParseCompileUnitDebugMacros (const SymbolContext &sc)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ParseCompileUnitDebugMacros(sc);
    }
    return false;
}
bool
SymbolVendor::ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList& support_files)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ParseCompileUnitSupportFiles(sc, support_files);
    }
    return false;
}

bool
SymbolVendor::ParseImportedModules (const SymbolContext &sc,
                                    std::vector<ConstString> &imported_modules)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ParseImportedModules(sc, imported_modules);
    }
    return false;
}

size_t
SymbolVendor::ParseFunctionBlocks (const SymbolContext &sc)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ParseFunctionBlocks(sc);
    }
    return 0;
}

size_t
SymbolVendor::ParseTypes (const SymbolContext &sc)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ParseTypes(sc);
    }
    return 0;
}

size_t
SymbolVendor::ParseVariablesForContext (const SymbolContext& sc)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ParseVariablesForContext(sc);
    }
    return 0;
}

Type*
SymbolVendor::ResolveTypeUID(lldb::user_id_t type_uid)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ResolveTypeUID(type_uid);
    }
    return nullptr;
}


uint32_t
SymbolVendor::ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ResolveSymbolContext(so_addr, resolve_scope, sc);
    }
    return 0;
}

uint32_t
SymbolVendor::ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->ResolveSymbolContext(file_spec, line, check_inlines, resolve_scope, sc_list);
    }
    return 0;
}

size_t
SymbolVendor::FindGlobalVariables (const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, size_t max_matches, VariableList& variables)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->FindGlobalVariables(name, parent_decl_ctx, append, max_matches, variables);
    }
    return 0;
}

size_t
SymbolVendor::FindGlobalVariables (const RegularExpression& regex, bool append, size_t max_matches, VariableList& variables)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->FindGlobalVariables(regex, append, max_matches, variables);
    }
    return 0;
}

size_t
SymbolVendor::FindFunctions(const ConstString &name, const CompilerDeclContext *parent_decl_ctx, uint32_t name_type_mask, bool include_inlines, bool append, SymbolContextList& sc_list)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->FindFunctions(name, parent_decl_ctx, name_type_mask, include_inlines, append, sc_list);
    }
    return 0;
}

size_t
SymbolVendor::FindFunctions(const RegularExpression& regex, bool include_inlines, bool append, SymbolContextList& sc_list)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->FindFunctions(regex, include_inlines, append, sc_list);
    }
    return 0;
}


size_t
SymbolVendor::FindTypes (const SymbolContext& sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, size_t max_matches, llvm::DenseSet<lldb_private::SymbolFile *> &searched_symbol_files, TypeMap& types)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->FindTypes(sc, name, parent_decl_ctx, append, max_matches, searched_symbol_files, types);
    }
    if (!append)
        types.Clear();
    return 0;
}

size_t
SymbolVendor::FindTypes (const std::vector<CompilerContext> &context, bool append, TypeMap& types)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->FindTypes(context, append, types);
    }
    if (!append)
        types.Clear();
    return 0;
}

size_t
SymbolVendor::GetTypes (SymbolContextScope *sc_scope,
                        uint32_t type_mask,
                        lldb_private::TypeList &type_list)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            return m_sym_file_ap->GetTypes (sc_scope, type_mask, type_list);
    }
    return 0;
}

CompilerDeclContext
SymbolVendor::FindNamespace(const SymbolContext& sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx)
{
    CompilerDeclContext namespace_decl_ctx;
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        if (m_sym_file_ap.get())
            namespace_decl_ctx = m_sym_file_ap->FindNamespace (sc, name, parent_decl_ctx);
    }
    return namespace_decl_ctx;
}

void
SymbolVendor::Dump(Stream *s)
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());

        bool show_context = false;

        s->Printf("%p: ", static_cast<void*>(this));
        s->Indent();
        s->PutCString("SymbolVendor");
        if (m_sym_file_ap.get())
        {
            ObjectFile *objfile = m_sym_file_ap->GetObjectFile();
            if (objfile)
            {
                const FileSpec &objfile_file_spec = objfile->GetFileSpec();
                if (objfile_file_spec)
                {
                    s->PutCString(" (");
                    objfile_file_spec.Dump(s);
                    s->PutChar(')');
                }
            }
        }
        s->EOL();
        s->IndentMore();
        m_type_list.Dump(s, show_context);

        CompileUnitConstIter cu_pos, cu_end;
        cu_end = m_compile_units.end();
        for (cu_pos = m_compile_units.begin(); cu_pos != cu_end; ++cu_pos)
        {
            // We currently only dump the compile units that have been parsed
            if (cu_pos->get())
                (*cu_pos)->Dump(s, show_context);
        }

        s->IndentLess();
    }
}

CompUnitSP
SymbolVendor::GetCompileUnitAtIndex(size_t idx)
{
    CompUnitSP cu_sp;
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        lldb_private::Mutex::Locker locker(module_sp->GetMutex());
        const size_t num_compile_units = GetNumCompileUnits();
        if (idx < num_compile_units)
        {
            cu_sp = m_compile_units[idx];
            if (cu_sp.get() == nullptr)
            {
                m_compile_units[idx] = m_sym_file_ap->ParseCompileUnitAtIndex(idx);
                cu_sp = m_compile_units[idx];
            }
        }
    }
    return cu_sp;
}

FileSpec
SymbolVendor::GetMainFileSpec() const
{
    if (m_sym_file_ap.get())
    {
        const ObjectFile *symfile_objfile = m_sym_file_ap->GetObjectFile();
        if (symfile_objfile)
            return symfile_objfile->GetFileSpec();
    }

    return FileSpec();
}

Symtab *
SymbolVendor::GetSymtab ()
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        ObjectFile *objfile = module_sp->GetObjectFile();
        if (objfile)
        {
            // Get symbol table from unified section list.
            return objfile->GetSymtab ();
        }
    }
    return nullptr;
}

void
SymbolVendor::ClearSymtab()
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        ObjectFile *objfile = module_sp->GetObjectFile();
        if (objfile)
        {
            // Clear symbol table from unified section list.
            objfile->ClearSymtab ();
        }
    }
}

bool
SymbolVendor::GetCompileOption(const char *option, std::string &value, lldb_private::CompileUnit *cu)
{
    SymbolFile *sym_file = GetSymbolFile();
    
    if (sym_file)
        return sym_file->GetCompileOption(option, value, cu);
        
    value.clear();
    return false;
}


int
SymbolVendor::GetCompileOptions(const char *option, std::vector<std::string> &values, lldb_private::CompileUnit *cu)
{
    SymbolFile *sym_file = GetSymbolFile();
    
    if (sym_file)
        return sym_file->GetCompileOptions(option, values, cu);
    
    values.clear();
    return false;
}

void
SymbolVendor::GetLoadedModules(lldb::LanguageType language, FileSpecList &modules)
{
    SymbolFile *sym_file = GetSymbolFile();
    
    if (sym_file)
        sym_file->GetLoadedModules(language, modules);
}

void
SymbolVendor::SectionFileAddressesChanged ()
{
    ModuleSP module_sp(GetModule());
    if (module_sp)
    {
        ObjectFile *module_objfile = module_sp->GetObjectFile ();
        if (m_sym_file_ap.get())
        {
            ObjectFile *symfile_objfile = m_sym_file_ap->GetObjectFile ();
            if (symfile_objfile != module_objfile)
                symfile_objfile->SectionFileAddressesChanged ();
        }
        Symtab *symtab = GetSymtab ();
        if (symtab)
        {
            symtab->SectionFileAddressesChanged ();
        }
    }
}

//------------------------------------------------------------------
// PluginInterface protocol
//------------------------------------------------------------------
lldb_private::ConstString
SymbolVendor::GetPluginName()
{
    static ConstString g_name("vendor-default");
    return g_name;
}

uint32_t
SymbolVendor::GetPluginVersion()
{
    return 1;
}

bool
SymbolVendor::SetLimitSourceFileRange (const FileSpec &file,
                                       uint32_t first_line,
                                       uint32_t last_line)
{
    SymbolFile *sym_file = GetSymbolFile();
    
    if (sym_file)
        return sym_file->SetLimitSourceFileRange (file, first_line, last_line);

    return false;
}

bool
SymbolVendor::SymbolContextShouldBeExcluded (const SymbolContext &sc, uint32_t actual_line)
{
    SymbolFile *sym_file = GetSymbolFile();
    
    if (sym_file)
        return sym_file->SymbolContextShouldBeExcluded (sc, actual_line);
    
    return false;
}

DataBufferSP
SymbolVendor::GetASTData (lldb::LanguageType language)
{
    // Sometimes the AST Section data is found from the module, so look there first:
    SectionList *section_list = GetModule()->GetSectionList();
    if (language != eLanguageTypeSwift)
        return DataBufferSP();

    if (section_list)
    {
        SectionSP section_sp (section_list->FindSectionByType (eSectionTypeSwiftModules, true));
        if (section_sp)
        {
            DataExtractor section_data;
            
            if (section_sp->GetSectionData(section_data))
            {
                return DataBufferSP (new DataBufferHeap((const char *)section_data.GetDataStart(), section_data.GetByteSize()));
            }
        }
    }

    // If we couldn't find it in the Module, then look for it in the SymbolFile:
    SymbolFile *sym_file = GetSymbolFile();

    if (sym_file)
        return sym_file->GetASTData (language);

    return DataBufferSP();
}


bool
SymbolVendor::ForceInlineSourceFileCheck ()
{
    SymbolFile *sym_file = GetSymbolFile();
    if (sym_file)
        return sym_file->ForceInlineSourceFileCheck();
    
    return false;
}
